package in.waffl.fairycake;

import java.awt.Rectangle;
import java.util.concurrent.ConcurrentLinkedQueue;

public abstract class Character implements Pollable {
	public static enum Direction {
		LEFT,
		RIGHT
	}
	private Direction direction;
	private int x;
	private int y;
	private int width;
	private int height;
	private float hspeed = 0;
	private float vspeed = 0;
	private float gravity = 0.2f;
	private static final int walkSpeed = 2;
	protected static final float jumpSpeed = -8;
	protected float hspeedTotal = 0;
	protected float vspeedTotal = 0;
	protected Drawable drawable;
	private Sprite sprite = null;
	private int imageIndex = 0;
	protected int walkAnim = 0;
	private boolean destroyed = false;
	private static final ConcurrentLinkedQueue<Character> characters = new ConcurrentLinkedQueue<Character>();
	private boolean hasMovedH = false;
	private boolean pushing = false;
	private boolean directionSet = false;

	public Character(int x, int y, int width, int height) {
		this.setX(x);
		this.setY(y);
		this.setWidth(width);
		this.setHeight(height);
		characters.offer(this);
		Main.poller.addPollable(this);
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
		hasMovedH = true;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}

	public int getWidth() {
		return width;
	}

	public int getHeight() {
		return height;
	}

	public int getImageIndex() {
		return imageIndex;
	}

	public void setImageIndex(int imageIndex) {
		this.imageIndex = imageIndex;
	}
	
	@Override
	public void polled(PollingThread p) {
		hasMovedH = false;
		hspeedTotal += getHspeed();
		vspeedTotal += getVspeed();
		Block hCollision = getCollisionRight();
		if (getHspeed() < 0) hCollision = getCollisionLeft();
		if (Math.abs(hspeedTotal) >= 1) {
			if ((hCollision == null || !hCollision.isEdge())) {
			setX(getX()
					+ Math.round(Math.abs(hspeedTotal)
							* (hspeedTotal > 0 ? 1 : -1)));
			} else {
				if (Projectile.class.isInstance(this)) {
					this.destroy();
				}
			}
			hspeedTotal = 0;
		}
		if (Math.abs(vspeedTotal) >= 1) {
			setY(getY()
					+ Math.round(Math.abs(vspeedTotal)
							* (vspeedTotal > 0 ? 1 : -1)));
			vspeedTotal = 0;
		}

		setVspeed(getVspeed() + getGravity());
		if (getVspeed() < 0) setVspeed(getVspeed() + getGravity()); //simulate friction on up-jump
		Rectangle collisionRect = new Rectangle(getX() + 10, getY() + getHeight(), getWidth() - 20, 1 + Math.round(getVspeed()));
		Block nextCollision = Block.collisionRectangleTop(collisionRect);
		if (nextCollision != null && !nextCollision.isEdge() && getVspeed() > 0) {
			setVspeed(0);
			setHspeed(0);
			setY(nextCollision.getY() - getHeight());
			onLanding();
		}
		characterPoll(p);
		pushing = false;
		directionSet = false;
	}
	
	protected void onLanding() {
		
	}
	
	public Block getCollisionLeft() {
		return Block.collisionRectangle(new Rectangle(getX()-2, getY(), 2, getHeight()));
	}
	
	public Block getCollisionRight() {
		return Block.collisionRectangle(new Rectangle(getX()+getWidth(), getY(), 2, getHeight()));
	}
	
	protected abstract void characterPoll(PollingThread p);
	
	public void destroy() {
		this.setDestroyed(true);
		if (drawable != null) drawable.destroy = true;
		drawable = null;
		Main.poller.removePollable(this);
		onDestroy();
	}
	
	protected void onDestroy() {}

	public void setWidth(int width) {
		this.width = width;
	}

	public void setHeight(int height) {
		this.height = height;
	}

	public float getGravity() {
		return gravity;
	}

	public void setGravity(float gravity) {
		this.gravity = gravity;
	}

	public float getHspeed() {
		return hspeed;
	}

	public void setHspeed(float hspeed) {
		this.hspeed = hspeed;
	}

	public float getVspeed() {
		return vspeed;
	}

	public void setVspeed(float vspeed) {
		this.vspeed = vspeed;
	}

	public Sprite getSprite() {
		return sprite;
	}

	public void setSprite(Sprite sprite) {
		this.sprite = sprite;
	}

	public Direction getDirection() {
		return direction;
	}

	public void setDirection(Direction direction) {
		this.direction = direction;
		directionSet = true;
	}
	
	public void die() {
		onDie();
		destroy();
	}
	
	protected void onDie() {}

	public boolean isDestroyed() {
		return destroyed;
	}

	private void setDestroyed(boolean destroyed) {
		this.destroyed = destroyed;
	}
	
	//last resort method if the characters haven't been destroyed for other reasons
	public static void destroyAll() {
			while (!characters.isEmpty()) {
				Character c = characters.poll();
				if (!c.isDestroyed()) c.destroy();
			}
			characters.clear();
	}

	protected static int getWalkSpeed() {
		return walkSpeed;
	}
	
	public boolean hasMovedH() {
		return hasMovedH;
	}
	
	public void setPushing() {
		pushing = true;
	}
	
	public boolean isPushing() {
		return pushing;
	}
	
	public void reverseDirection() {
		if (getDirection() == Direction.LEFT) {
			setDirection(Direction.RIGHT);
		} else {
			setDirection(Direction.LEFT);
		}
	}
	
	public boolean isDirectionSet() {
		return directionSet;
	}
}